/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package stattracker;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Properties;
import java.util.TreeMap;
/**
 *
 * @author mattcrain 
 */
public class BaseSystem 
{
	private static final String PROPERTY_FILE = "properties.stt";
	private static final String DEFAULT_GT = "defaultgt";
	private static final String DEFAULT_LIST = "defaultlist";
	private static final String AUDIO_LOCATION = "audiolocation";
	
	private StatTracker parent;
	private Gamertag selectedGamer;
    private ArrayList<Gamertag> gamers;
    private ArrayList<String> teams;
    private TreeMap<String, Gamertag> gamerMap;
    private TreeMap<String, ArrayList<String>> teamMap;
    private String currentSave;
    private int working;
    private boolean isSaving;
    private Properties userProps;
    private Database db;
    private boolean connected;
    
    public BaseSystem()
    {
    	try {
			db = Database.getInstance();
			connected = true;
		} catch (DatabaseInaccessibleException e) {
			new ErrorMessage("Couldn't connect to the Database. You won't be able to access online features.");
			connected = false;
		}
        gamers = new ArrayList<Gamertag>();
        gamerMap = new TreeMap<String, Gamertag>();
        teams = new ArrayList<String>();
        teamMap = new TreeMap<String, ArrayList<String>>();
        userProps = new Properties();
        currentSave = "";
        working = 0;
        selectedGamer = null;
        isSaving = false;
        parent = null;
    }
    
    public void loadUserProps()
    {
    	if(!new File(PROPERTY_FILE).exists()){
    		propInit();
    	}
    	else{
	    	FileInputStream fileIn;
			try {
				fileIn = new FileInputStream(PROPERTY_FILE);
				userProps.load(fileIn);
			} catch (FileNotFoundException e) {
				new ErrorMessage("Couldn't load the Properties File.");
				propInit();
			} catch (IOException e) {
				new ErrorMessage("Couldn't load the Properties File: IOException");
				propInit();
			} 
    	}
    	checkDefaults();
    }
    
    private void checkDefaults(){
    	String listLoc = userProps.getProperty(DEFAULT_LIST);
    	if(!MyUtils.emptyString(listLoc)){
    		if(new File(listLoc).exists()){
        		loadSystem(listLoc);
        	}
        	else{
        		clearDefaultList();
        	}
    	}
    	String defaultGamer = userProps.getProperty(DEFAULT_GT);
    	if(!MyUtils.emptyString(defaultGamer)){
    		selectGamer(defaultGamer);
    	}
    }
    
    public String getDefaultGT(){
    	return userProps.getProperty(DEFAULT_GT);
    }
    
    private void propInit(){
    	userProps = new Properties();
		clearAllPreferences();
    }
    
    public void clearAllPreferences(){
    	clearDefaultGT();
    	clearDefaultList();
    	clearAudioLocation();
    }
    
    public void clearDefaultGT(){
    	userProps.setProperty(DEFAULT_GT, "");
    	saveProperties();
    }
    
    public void clearDefaultList(){
    	userProps.setProperty(DEFAULT_LIST, "");
    	saveProperties();
    }
    
    public void clearAudioLocation(){
    	userProps.setProperty(AUDIO_LOCATION, "");
    	saveProperties();
    }
    
    public void setDefaultGT(String gt){
    	if(!gamerMap.containsKey(gt)){
    		addGamer(gt);
    	}
    	userProps.setProperty(DEFAULT_GT, gt);
    	saveProperties();
    }
    
    public void setDefaultList(String fileLoc){
    	if(new File(fileLoc).exists()){
    		userProps.setProperty(DEFAULT_LIST, fileLoc);
    	}
    	saveProperties();
    }
    
    public void setAudioLocation(String filePath){
    	String audio = filePath + "extermination.wav";
    	if(new File(audio).exists()){
    		userProps.setProperty(AUDIO_LOCATION, filePath);
    	}
    	saveProperties();
    }
    
    public void displaySelectedHalo3Data()
    {
    	Halo3Data data = loadHalo3Data(getSelectedName(), false);
    	new Halo3Frame(data);
    }
    
    public void displayHalo3Team(String teamName)
    {
    	Halo3Team team = loadHalo3Team(teamName);
    	new Halo3TeamFrame(team);
    }
    
    public void createHalo3Team(String teamName, ArrayList<String> names)
    {
    	ArrayList<Halo3TeamMember> team = new ArrayList<Halo3TeamMember>(names.size());
    	for(String s : names)
    		team.add(new Halo3TeamMember(s));
    	
    	Halo3Team created = new Halo3Team(teamName, team);
    	created.update(this);
    	parent.updateTitle("Saving Team");
    	saveHalo3Team(created);
    	teams.add(teamName);
    	teamMap.put(teamName, names);
    }
    
    public void updateHalo3Team(String teamName)
    {
    	parent.updateTitle("Updating Team Data for: " + teamName);
    	Halo3Team team = loadHalo3Team(teamName);
    	team.update(this);
    	saveHalo3Team(team);
    	parent.updateTitle("Update Complete!");
    }
    
    public boolean teamNameTaken(String name)
    {
    	return teamMap.containsKey(name);
    }
    
    public void getSelectedHalo3Data(boolean getGames)
    {
    	String fileLoc = getSelectedName()+".halo3";
    	if(!new File(fileLoc).exists())
		{
			saveHalo3Data(new Halo3Data(getSelectedName()), false);
		}
    	boolean tryDB = false;
    	if(db.hasNewerSaveFile(getSelectedName()))
    	{
    		tryDB = parent.downloadH3SaveFromDB();
    	}
    	Halo3Data playerData = loadHalo3Data(getSelectedName(), tryDB);
    	if(playerData != null)
    	{
    		if(getGames)
    		{
	    		new Halo3GamesFetcher(playerData, parent, false);
	    		wait(playerData);
	    		parent.updateTitle("Saving Matchmade Games");
	    		saveHalo3Data(playerData, false);
	    		new Halo3GamesFetcher(playerData, parent, true);
	    		wait(playerData);
	    		parent.updateTitle("Saving Custom Games");
	    		saveHalo3Data(playerData, false);
	    		parent.updateTitle("Getting Images");
	    		new Halo3ImageFetcher(playerData.getImages());
    		}
    		parent.updateTitle("Calculating " + playerData.getGamertag() + "'s Halo 3 Statistics...");
    		playerData.compileStatistics();
    		saveHalo3Data(playerData, true);
    		parent.updateTitle(playerData.getGamertag() + " Halo 3 Data Retrieval Complete!");
    	}
    	else
    	{
    		new ErrorMessage("PC Load Letter Error");
    	}
    }
    
    public void getSelectedHaloReachBetaData(boolean getGames) 
    {
    	String fileLoc = getSelectedName()+".reach";
		if(!new File(fileLoc).exists())
		{
			saveReachBetaData(new ReachBetaData(getSelectedName()));
		}
		ReachBetaData playerData = loadReachBetaData(getSelectedName());
		if(playerData != null)
		{
			if(getGames)
			{
				new ReachBetaGamesFetcher(playerData);
				wait(playerData);
			}
			//For doing statistics later
			parent.updateTitle("Halo: Reach Beta Data Retrieval Complete!");
		}
		else
		{
			new ErrorMessage("PC Load Letter Error");
		}
	}
    
    private void wait(GameData playerData)
    {
    	while(playerData.isUpdating())
		{
			try
			{
				Thread.sleep(1000); //pauses for a second before checking again
			}
			catch(InterruptedException e){new ErrorMessage(e);}
		}
    }
    
    public void waitOnWork()
    {
    	while(isWorking())
    	{
    		try
			{
				Thread.sleep(1000); //pauses for a second before checking again
			}
			catch(InterruptedException e){new ErrorMessage(e);}
    	}
    }
    
    public Halo3Team loadHalo3Team(String teamName)
    {
    	try
    	{
    		String fileLoc = teamName + ".h3team";
    		if(!new File(fileLoc).exists())
			{
				new ErrorMessage("File Specified Does Not Exist.\n" + fileLoc);
			}
			FileInputStream fileIn = new FileInputStream(fileLoc);
            ObjectInputStream in = new ObjectInputStream(fileIn);
            Halo3Team team = (Halo3Team)in.readObject();
            in.close();
            return team;
    	}
    	catch(IOException e)
    	{
    		new ErrorMessage(e);
    	}
    	catch (ClassNotFoundException e) 
		{
			new ErrorMessage(e);
		}
		return null;
    }
    
    public Halo3Data loadHalo3Data(String gamertag, boolean fromDB)
    {
    	try
		{
    		boolean success = false;
    		if(fromDB && connected && db.isConnected())
    		{
    			int gtID = db.getGamertagId(gamertag);
	        	if(gtID != Database.INVALID_ID)
	        	{
		        	success = db.getHalo3SaveFile(gtID);
	        	}
    		}
    		
			String fileLoc = gamertag + ".halo3";
			if(success)
				fileLoc = "temp.halo3";
			if(!new File(fileLoc).exists())
			{
				new ErrorMessage("File Specified Does Not Exist.\n" + fileLoc);
			}
			FileInputStream fileIn = new FileInputStream(fileLoc);
            ObjectInputStream in = new ObjectInputStream(fileIn);
            Halo3Data data = (Halo3Data)in.readObject();
            in.close();
            return data;
		}
		catch(IOException e)
		{
        	new ErrorMessage(e);
        } 
		catch (ClassNotFoundException e) 
		{
			new ErrorMessage(e);
		}
		return null;
    }
    
    public ReachBetaData loadReachBetaData(String gamertag)
    {
    	try
		{
    		String fileLoc = gamertag + ".reach";
			if(!new File(fileLoc).exists())
			{
				new ErrorMessage("File Specified Does Not Exist.\n" + fileLoc);
			}
			FileInputStream fileIn = new FileInputStream(fileLoc);
            ObjectInputStream in = new ObjectInputStream(fileIn);
            ReachBetaData data = (ReachBetaData)in.readObject();
            in.close();
            return data;
		}
		catch(IOException e)
		{
        	new ErrorMessage(e);
        } 
		catch (ClassNotFoundException e) 
		{
			new ErrorMessage(e);
		}
		return null;
    }
    
    public void setParent(StatTracker p)
    {
    	parent = p;
    }
    
    private void saveHalo3Team(Halo3Team toSave)
    {
    	try
    	{
    		isSaving = true;
    		FileOutputStream fileOut = new FileOutputStream(toSave.getName()+".h3team");
    		ObjectOutputStream out = new ObjectOutputStream(fileOut);
    		out.writeObject(toSave);
    		out.close();
    		isSaving = false;
    	}
    	catch(IOException e)
    	{
    		new ErrorMessage(e);
    	}
    }
    
    public File getHalo3DataFile(String gamertag)
    {
    	return new File(gamertag + ".halo3");
    }
    
    private void saveHalo3Data(Halo3Data toSave, boolean dbUpload)
    {
    	try
		{
    		isSaving = true;
    		String saveFile = toSave.getGamertag()+".halo3";
			FileOutputStream fileOut = new FileOutputStream(saveFile);
	        ObjectOutputStream out = new ObjectOutputStream(fileOut);
	        out.writeObject(toSave);
	        out.close();
	        if(dbUpload && connected && db.isConnected())
	        {
	        	parent.updateTitle(String.format("Uploading %s Save Data to Database...", toSave.getGamertag()));
	        	int gtID = db.getGamertagId(toSave.getGamertag());
	        	if(gtID != Database.INVALID_ID)
	        	{
		        	db.insertHalo3SaveFile(gtID, saveFile);
		        	parent.updateTitle("Upload Complete!");
	        	}
	        }
	        isSaving = false;
		}
		catch(IOException e)
		{
        	new ErrorMessage(e);
        }
    }
    
    private void saveReachBetaData(ReachBetaData toSave)
    {
    	try
		{
    		isSaving = true;
			FileOutputStream fileOut = new FileOutputStream(toSave.getGamertag()+".reach");
	        ObjectOutputStream out = new ObjectOutputStream(fileOut);
	        out.writeObject(toSave);
	        out.close();
	        isSaving = false;
		}
		catch(IOException e)
		{
        	new ErrorMessage(e);
        }
    }
    
    private void deleteHalo3Data(String fileLoc)
    {
    	if(!new File(fileLoc).delete())
    		new ErrorMessage("File Not Found: " + fileLoc);
    }
    
    public String getSelectedName()
    {
    	if(hasGamerSelected())
    		return selectedGamer.getGamertag();
    	return "";
    }
    
    public boolean hasGamerSelected()
    {
    	return selectedGamer != null;
    }
    
    public void sortGamers()
    {
    	Collections.sort(gamers);
    }
    
    public void sortTeams()
    {
    	Collections.sort(teams);
    }
    
    public Gamertag lookup(String gt)
    {
    	return gamerMap.get(gt);
    }
    
    public ArrayList<Gamertag> getGamers()
    {
        return gamers;
    }
    
    public ArrayList<String> getTeams()
    {
    	return teams;
    }
    
    public ArrayList<String> getTeammates(String teamName)
    {
    	if(!teamMap.containsKey(teamName))
    		return new ArrayList<String>();
    	return teamMap.get(teamName);
    }
    
    public boolean addGamer(String gamertag)
    {
    	if(gamerMap.containsKey(gamertag))
    		return true;
    	gamertag = MyUtils.BungieCheck(gamertag);
        if(!gamertag.equals(MyUtils.INVALID_GT))
        {
        	Gamertag newGamer = new Gamertag(gamertag);
        	gamerMap.put(gamertag, newGamer);
            gamers.add(newGamer);
            return true;
        }
        return false;
    }
    
    public boolean removeTeam(String teamName, boolean removeGameData)
    {
    	String fileLoc = teamName+".h3team";
    	if(removeGameData)
    		deleteHalo3Data(fileLoc);
    	if(teamMap.containsKey(teamName))
    		teamMap.remove(teamName);
    	else
    		return false;
    	for(int i = 0; i < teams.size(); i++)
    	{
    		if(teams.get(i).equals(teamName))
    		{
    			teams.remove(i);
    			return true;
    		}
    	}
    	return false;
    }
    
    public boolean removeGamer(String gamertag, boolean removeGameData)
    {
    	if(removeGameData)
    	{
    		deleteHalo3Data(gamertag+".halo3");
    	}
    	if(gamertag.equals(getSelectedName()))
    		selectedGamer = null; //Clears current selection to allow garbage collection
    	if(gamerMap.containsKey(gamertag))
    		gamerMap.remove(gamertag);
    	else
    		return false;
    	for(int i = 0; i < gamers.size(); i++)
    	{
    		if(gamers.get(i).getGamertag().equals(gamertag))
    		{
    			gamers.remove(i);
    			return true;
    		}
    	}
    	return false;
    }
    
    public boolean hasTeamFile(String teamName)
    {
    	return new File(teamName+".h3team").exists();
    }
    
    public boolean hasGameFile(String gamertag)
    {
    	return new File(gamertag + ".halo3").exists();
    }
    
    public boolean hasSaveLocation()
    {
    	return currentSave.length() > 2;
    }
    
    public boolean isWorking()
    {
    	return working > 0;
    }
    
    public void addWorker()
    {
    	working++;
    }
    
    public void workerDone()
    {
    	working--;
    }
    
    public boolean selectGamer(String name)
    {
    	if(gamerMap.containsKey(name))
    	{
    		selectedGamer = gamerMap.get(name);
    		return true;
    	}
    	return false;
    }
    
    public boolean isSaving()
    {
    	return isSaving;
    }
    
    public void saveSystem(String fileLoc)
    {
    	isSaving = true;
    	currentSave = fileLoc;
    	quickSave();
    	isSaving = false;
    }
    
    public void quickSave()
    {
    	try
		{
    		isSaving = true;
			FileOutputStream fileOut = new FileOutputStream(currentSave);
	        ObjectOutputStream out = new ObjectOutputStream(fileOut);
	        out.writeObject(gamers);
	        out.writeObject(gamerMap);
	        out.writeObject(teams);
	        out.writeObject(teamMap);
	        out.close();
	        this.setDefaultList(currentSave);
	        saveProperties();
	        isSaving = false;
		}
		catch(IOException e)
		{
        	new ErrorMessage(e);
        }
    }

	private void saveProperties() 
	{
		isSaving = true;
		try
		{
			FileOutputStream fileOut;
			fileOut = new FileOutputStream(PROPERTY_FILE);
			userProps.store(fileOut, PROPERTY_FILE);
			fileOut.close();
		}
		catch(FileNotFoundException e)
		{
			new ErrorMessage(e);
		}
		catch(IOException e)
		{
			new ErrorMessage(e);
		}
		finally
		{
			isSaving = false;
		}
	}
    
	@SuppressWarnings("unchecked")
    public void loadSystem(String fileLoc)
    {
		try
		{
			if(!new File(fileLoc).exists())
			{
				new ErrorMessage("File Specified Does Not Exist.");
				return;
			}
			currentSave = fileLoc;
			FileInputStream fileIn = new FileInputStream(fileLoc);
            ObjectInputStream in = new ObjectInputStream(fileIn);
            gamers = (ArrayList<Gamertag>)in.readObject();
            gamerMap = (TreeMap<String, Gamertag>)in.readObject();
            teams = (ArrayList<String>)in.readObject();
            teamMap = (TreeMap<String, ArrayList<String>>)in.readObject();
            in.close();
            
            if(MyUtils.emptyString(userProps.getProperty(DEFAULT_LIST))){
            	setDefaultList(fileLoc);
            }
		}
		catch(Exception e)
		{
        	new ErrorMessage(e);
        }
    }
}
